package com.xuecheng.media.service.impl;

import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.qo.QueryCourseModel;
import com.xuecheng.api.media.model.aliyun.dto.MediaDTO;
import com.xuecheng.api.media.model.aliyun.qo.QueryMediaModel;
import com.xuecheng.api.media.model.aliyun.vo.MediaVO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.IPUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.media.common.agent.ContentApiAgent;
import com.xuecheng.media.common.constant.MediaErrorCode;
import com.xuecheng.media.common.utils.AliyunVODUtil;
import com.xuecheng.media.controller.MediaAuditController;
import com.xuecheng.media.convert.MediaConvert;
import com.xuecheng.media.entity.Media;
import com.xuecheng.media.mapper.MediaMapper;
import com.xuecheng.media.service.MediaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 媒资信息 服务实现类
 * </p>
 *
 * @author dongliang
 */
@Slf4j
@Service
public class MediaServiceImpl extends ServiceImpl<MediaMapper, Media> implements MediaService {

    @Autowired
    private ContentApiAgent contentApiAgent;

    @Value("${aliyun.region}")
    private String region;
    @Value("${aliyun.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyun.accessKeySecret}")
    private String accessKeySecret;

    /*
     * 业务分析：
     *   1.判断关键
     *       companyid  filename  fileid(videoId)
     *   2.给信息数据赋值默认值
     *       auditStatus：未审核
     *
     *   3.保存信息
     *   4.将数据库最新数据返回
     *
     * */
    @Transactional
    public MediaDTO createMedia(MediaDTO dto) {

        //1.判断关键
        //     companyid  filename  fileid(videoId)
        if (ObjectUtils.isEmpty(dto.getCompanyId()) ||
                StringUtil.isBlank(dto.getFilename()) ||
                StringUtil.isBlank(dto.getFileId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        // 2.给信息数据赋值默认值
        //     auditStatus：未审核
        dto.setAuditStatus(AuditEnum.AUDIT_UNPAST_STATUS.getCode());


        // 3.保存信息
        Media media = MediaConvert.INSTANCE.dto2entity(dto);
        boolean result = this.save(media);

        if (!result) {
            ExceptionCast.cast(MediaErrorCode.E_140001);
        }

        // 4.将数据库最新数据返回
        Media po = this.getById(media.getId());
        MediaDTO resultDTO = MediaConvert.INSTANCE.entity2dto(po);

        return resultDTO;
    }

    /**
     * 带条件分页查询
     *
     * @param params          封装分页数据
     * @param queryMediaModel 前端传入课程基础信息 Qo
     * @return PageVO 分页结果封装
     */
    public PageVO<MediaDTO> queryMediaList(PageRequestParams params, QueryMediaModel queryMediaModel, Long companyId) {

        //1判断分页数据 每页条数 大于0
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }


        //2构建查询条件对象LambdaQueryWrapper
        LambdaQueryWrapper<Media> queryWrapper = new LambdaQueryWrapper<>();
        //相当于SELECT COUNT(*) FROM course_base WHERE (name LIKE '%SpringBoot%' AND audit_status = '202004' AND company_id = '1232141425')
        //课程名不为null时模糊查询
        queryWrapper.like(StringUtil.isNotBlank(queryMediaModel.getFilename()),
                Media::getFilename, queryMediaModel.getFilename());
        //状态不为null时,条件查询
        queryWrapper.eq(StringUtil.isNotBlank(queryMediaModel.getType()),
                Media::getType, queryMediaModel.getType());
        queryWrapper.eq(!ObjectUtils.nullSafeEquals(companyId, MediaAuditController.OPERATION_FLAG),
                Media::getCompanyId, companyId);
        queryWrapper.eq(Media::getStatus, CommonEnum.USING_FLAG.getCode());
        //添加对媒资审核状态的条件
        queryWrapper.eq(StringUtils.isNotBlank(queryMediaModel.getAuditStatus()),
                Media::getAuditStatus, queryMediaModel.getAuditStatus());

        //3构建mp分页对象
        Page<Media> page = new Page<>(params.getPageNo(), params.getPageSize());

        //4.根据分页查询数据
        Page<Media> pageResult = this.page(page, queryWrapper);
        //5拿到分页好的records数据存放到List<CourseBase>
        List<Media> records = pageResult.getRecords();
        //拿到总条数
        long total = pageResult.getTotal();

        //6.将List<CourseBase>转换成List<CourseBaseDTO>
        List<MediaDTO> baseDTOList = Collections.emptyList();
        if (!CollectionUtils.isEmpty(records)) {
            baseDTOList = MediaConvert.INSTANCE.entitys2dtos(records);
        }

        //7返回结果封装PageVo数据
        PageVO pageVO = new PageVO(baseDTOList, total, params.getPageNo(), params.getPageSize());

        return pageVO;
    }


    /*
        * 业务分析：
        1.判断关键数据
        *   mediaId、companyId
        *
        * */
    @Transactional //1.开启事务
    public boolean removeMediaById(Long mediaId, Long companyId) {
        //2.判断关键数据 courseBaseId  companyId
        if (ObjectUtils.isEmpty(mediaId)) {
            ExceptionCast.cast(MediaErrorCode.E_140016);
        }
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(MediaErrorCode.E_140017);
        }
        /*2.判断业务数据
		媒资信息
		    判断是否存在
			判断审核状态：只有审核通过不能删除
			判断媒资信息是否有绑定课程计划数据
			绑定媒资和课程计划数据：content（发送feign）
				 teachplan_media
		*/

        LambdaQueryWrapper<Media> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Media::getId, mediaId);
        wrapper.eq(Media::getCompanyId, companyId);
        wrapper.eq(Media::getStatus, CommonEnum.USING_FLAG.getCode());
        //查询课程基础表来判断
        Media media = this.getOne(wrapper);

        verifyPO(companyId, media);

        //2.4 判断课程的审核状态
        String auditStatus = media.getAuditStatus();

        if (AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)) {
            ExceptionCast.cast(MediaErrorCode.E_140018);
        }
        //根据mediaid判断媒资信息是否有绑定课程计划数据
        Boolean flag = contentApiAgent.isRemoveTeachplanMedia(mediaId);

        //直接删除媒资 逻辑删除
        LambdaUpdateWrapper<Media> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Media::getStatus, CommonEnum.DELETE_FLAG.getCode());
        updateWrapper.eq(Media::getId, mediaId);
        boolean update = this.update(updateWrapper);
        if (!update) {
            ExceptionCast.cast(MediaErrorCode.E_140023);
        }
        try {
            //调用阿里云删除
            DefaultAcsClient client = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);

            AliyunVODUtil.deleteVideo(client, media.getFileId());
        } catch (Exception e) {
            ExceptionCast.cast(MediaErrorCode.E_140023);
        }
        return true;
    }

    private void verifyPO(Long companyId, Media media) {
        // 判断课程数据是否存在
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140001);
        }

        // 判断是否是同一家机构
        if (!(ObjectUtils.nullSafeEquals(companyId, media.getCompanyId()))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
    }

    @Transactional
    public void approve(MediaDTO dto) {
        /*
         * 业务分析：
         *   1.判断关键数据
         *       auditMind  auditStatus  courseid
         *
         *   2.判断业务数据
         *       课程基础信息
         *           判断是否存在
         *           判断是否删除
         *           判断审核状态
         *               必须为：已提交
         *       审核状态
         *           运营平台只能给课程审核状态赋值：审核通过、审核未通过
         *   3.修改课程审核信息
         *       auditMind  auditStatus  auditNum（每次+1）
         * */

        // 1.判断关键数据
        //      auditMind  auditStatus  courseid
        if (ObjectUtils.isEmpty(dto.getAuditMind()) ||
                ObjectUtils.isEmpty(dto.getAuditStatus()) ||
                ObjectUtils.isEmpty(dto.getId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //  2.判断业务数据
        //      课程基础信息
        //          判断是否存在
        //          判断是否删除
        //          判断审核状态
        //              必须为：已提交
        LambdaQueryWrapper<Media> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Media::getId, dto.getId());
        queryWrapper.eq(Media::getStatus, CommonEnum.USING_FLAG.getCode());
        queryWrapper.eq(Media::getAuditStatus, AuditEnum.AUDIT_UNPAST_STATUS.getCode());


        Media media = this.getOne(queryWrapper);


        //      审核状态
        //          运营平台只能给课程审核状态赋值：审核通过、审核未通过
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140020);
        }
        String status = media.getStatus();
        if (CommonEnum.DELETE_FLAG.getCode().equals(status)) {
            ExceptionCast.cast(MediaErrorCode.E_140020);
        }
        /*//查询数据库中的次数,存放到redis中
        redisTemplate.opsForValue().set("auditNum",courseBase.getAuditNums());

        //根据key对次数加+1返回的就是每次+1的次数
        Long auditNums = querryAuditNum();*/

        LambdaUpdateWrapper<Media> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Media::getAuditMind, dto.getAuditMind());
        updateWrapper.set(Media::getAuditStatus, dto.getAuditStatus());
//        updateWrapper.set(Media::getAuditNums,auditNums);

        updateWrapper.set(Media::getCreateDate, LocalDateTime.now());
        updateWrapper.eq(Media::getId, dto.getId());

        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(MediaErrorCode.E_140020);
        }
    }

    /*private Long querryAuditNum() {
        // TODO:redis完成 auditNum（每次+1）需求
        String key = "auditNum";
        Long liveTime=100L;
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始设置过期时间
            entityIdCounter.expire(liveTime, TimeUnit.HOURS);
        }
        return increment;
    }*/


    public String getVODUrl(Long mediaId, Long companyId) {

        // 1.判断业务数据
        if (ObjectUtils.isEmpty(mediaId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.根据媒资id获得媒资信息
        Media media = getById(mediaId);
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(CommonErrorCode.E_100104);
        }

        // 3.如果公司id不为空，对其进行判断
        if (!(ObjectUtils.isEmpty(companyId))) {
            if (!(ObjectUtils.nullSafeEquals(media.getCompanyId(), companyId))) {
                ExceptionCast.cast(CommonErrorCode.E_100108);
            }
        }

        String playUrl = getPlayUrl(media.getFileId());


        // 4.返回媒资的资源地址
        return playUrl;
    }


    private String getPlayUrl(String videoId) {

        String playUrl = "";
        try {

            DefaultAcsClient client =
                    AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);

            GetPlayInfoResponse response = AliyunVODUtil.getPlayInfo(client, videoId);

            List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();

            if (!(ObjectUtils.isEmpty(playInfoList))) {
                GetPlayInfoResponse.PlayInfo playInfo = playInfoList.get(0);
                playUrl = playInfo.getPlayURL();
            }

        } catch (Exception e) {
            ExceptionCast.cast(MediaErrorCode.E_140014);
        }
        return playUrl;
    }


    /*
     * 业务分析：
     *   PS：由于是学成内部微服务间的调用，此功能只需要提供基础数据即可，不需要加上业务逻辑判断
     *   如果为了数据的完整性，可以对传入和传出的参数进行判断
     *
     *   1.判断mediaid是否为空
     *   2.查询数据
     *   3.判断数据是否存在
     *   4.如果存在，使用RestResponse来封装返回
     * */
    public RestResponse<MediaDTO> getById4Service(Long mediaId) {

        // 1.判断关键数据
        if (ObjectUtils.isEmpty(mediaId)) {
            return RestResponse.validfail(CommonErrorCode.E_100101);
        }

        // 2.判断业务数据
        Media media = this.getById(mediaId);

        if (ObjectUtils.isEmpty(media)) {
            return RestResponse.validfail(MediaErrorCode.E_140005);
        } else {
            MediaDTO mediaDTO = MediaConvert.INSTANCE.entity2dto(media);
            return RestResponse.success(mediaDTO);
        }
    }
}